package rwt.controlpanel;

import java.io.*;
import java.util.concurrent.*;
import java.util.regex.*;

import javax.swing.JOptionPane;

/**
 * Miscellaneous utility methods for executing commands, killing processing, finding the PID of the server,
 * and more.
 */
public final class Util
{
	public static int PID_NOT_RUNNING = -1;
	
	private static final Pattern regex = Pattern.compile("(?<pid>\\d{1,6})\\s+" + Constants.WEB_SERVER_JAR_NAME,
			Pattern.CASE_INSENSITIVE);

	/**
	 * Executes a command on another thread, waiting at most 1 second. Does not return until execution
	 * completes, or a timeout occurs.
	 * 
	 * @param cmd The command to run, including any arguments.
	 * @return the standard-output of the command.
	 */
	public static String execCommand(String cmd)
	{
		return execCommand(cmd, 1000);
	}
	
	/**
	 * Executes a command on another thread, waiting at most <tt>timeoutMillis</tt>. Does not return until
	 * execution completes, or a timeout occurs.
	 * 
	 * @param cmd The command to run, including any arguments.
	 * @param timeoutMillis Maximum length of time (in milliseconds) to wait for the command to complete
	 *        execution.
	 * @return the standard-output of the command.
	 */
	public static String execCommand(String cmd, long timeoutMillis)
	{
		final String command = cmd;
		
		// The thread in which we will fork the process (so that we can enforce a timeout to prevent hanging). 
		Callable<String> task = new Callable<String>()
		{
			@Override
			public String call() throws Exception
			{
				try
				{
					Process p = Runtime.getRuntime().exec(command);
					String output = null;
					try (BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())))
					{
						StringBuilder sb = new StringBuilder();
						String line = null;
						while ((line = reader.readLine()) != null)
							sb.append(line + "\n");
						p.waitFor();
						output = sb.toString();
					}
					
					return output;
				}
				catch (Exception e)
				{
					e.printStackTrace();
				}
				
				return null;
			}
		};
		
		FutureTask<String> ft = new FutureTask<>(task);
		ft.run();
		
		try
		{
			String result = ft.get(timeoutMillis, TimeUnit.MILLISECONDS);
			return result;
		}
		catch (TimeoutException e)
		{
			System.err.println("Timed out while executing command '" + cmd+  "'." + 
					"  [Timeout=" + timeoutMillis + " ms]");
		}
		catch (Exception e)
		{
			System.err.println("Unexpected error while executing command '" + cmd + "'!");
			e.printStackTrace();
		}
		
		return null; // Error or timeout: assume process to not be running
	}
	
	public static int getServerPid()
	{
		// Maximum length of time (in milliseconds) to wait for 'tasklist' exec to finish (to avoid hanging)
		String output = execCommand("jps -l", 2000);
		if (output != null)
		{
			Matcher m = regex.matcher(output);
			if (m.find())
				return Integer.parseInt(m.group("pid"));
		}
		return PID_NOT_RUNNING;
	}
	
	/**
	 * Kills the process with the given PID.
	 * @param pid The PID of the process to kill.
	 */
	public static void killProcess(int pid)
	{
		String output = execCommand("taskkill /F /PID " + pid, 1000);
		System.out.println("Output from 'taskkill': " + output);
	}
	
	/**
	 * Show a UI error message with the indicated message, an error icon, and an OK button.
	 * @param msg The message to display in the error message.
	 */
	public static void showError(String msg)
	{
		JOptionPane.showMessageDialog(null, msg, "RWT Control Panel", JOptionPane.WARNING_MESSAGE);
	}

	/**
	 * Show a UI info message with the indicated message, an error icon, and an OK button.
	 * @param msg The message to display in the error message.
	 */
	public static void showMessage(String msg)
	{
		JOptionPane.showMessageDialog(null, msg, "RWT Control Panel", JOptionPane.INFORMATION_MESSAGE);
	}
}
